﻿#include <glad/glad.h>   // 管理OpenGL的函数指针
#include <GLFW/glfw3.h>  // 提供窗口和OpenGL context

#include <cmath>
#include <iostream>
#include <memory>

#include "mygl/shader.h"

#ifndef PROJECT_ROOT
#define PROJECT_ROOT "F:/VisualStudioProjects/my-open-gl"
#endif

// settings
const std::string kProjectRoot = PROJECT_ROOT + std::string("/");
const std::string kWorkSpaceRoot = kProjectRoot + "01-GettingStarted/02-ShaderLanguage/";
const std::string kShaderRoot = kWorkSpaceRoot + "";
const unsigned int kWindowWidth = 800;
const unsigned int kWindowHeight = 600;
const char* kWindowName = "ShaderLanguage";

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

int main() {
  // glfw: initialize and configure
  // ------------------------------
  glfwInit();
  // OpenGL 3.3
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);  // 核心模式

  // glfw window creation
  // --------------------
  GLFWwindow* window =
      glfwCreateWindow(kWindowWidth, kWindowHeight, kWindowName, NULL, NULL);
  if (window == NULL) {
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();
    return -1;
  }
  glfwMakeContextCurrent(window);
  glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

  // glad: load all OpenGL function pointers
  // 调用任何gl函数前，必须初始化glad
  if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
  }

  // MyProject
  // Load Shader, Compile, Link
  std::string vertex_path = kShaderRoot + "shader.vs";
  std::string fragment_path = kShaderRoot + "shader.fs";
  auto shader = std::make_unique<mygl::Shader>(vertex_path, fragment_path);
  if (shader->Init()) {
    return -1;
  }
  // clang-format off
  float vertices[] = {
      -0.5f, -0.5f, 0.0f,
      0.5f, -0.5f, 0.0f, 
      0.0f, 0.5f, 0.0f
  };
  //short indices[] = {
  //    0, 1, 3,
  //    1, 2, 3
  //};
  // clang-format on
  GLuint vao, vbo, ebo;
  // vao
  glGenVertexArrays(1, &vao);
  glBindVertexArray(vao);
  // vbo
  glGenBuffers(1, &vbo);
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  // ebo
  // glGenBuffers(1, &ebo);
  // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
  // glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GL_FLOAT), (void*)0);
  glEnableVertexAttribArray(0);

  glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

  glBindVertexArray(vao);  // 可以放到循环外

  // render loop
  // -----------
  while (!glfwWindowShouldClose(window)) {
    // input
    // -----
    processInput(window);

    // render
    // ------
    glClear(GL_COLOR_BUFFER_BIT);

    double time_value = glfwGetTime();
    float color_value = static_cast<float>(std::sin(time_value) / 2.0 + 0.5);
    float x_pos_value = static_cast<float>(std::sin(time_value));
    float y_pos_value = static_cast<float>(std::cos(time_value));
    // std::cout << color_value << std::endl;

    shader->UseProgram();
    shader->SetUniformFloat("time_color", color_value);
    shader->SetUniformVecnFloat("time_pos", 2, {x_pos_value, y_pos_value});

    glDrawArrays(GL_TRIANGLES, 0, 3);

    // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
    // -------------------------------------------------------------------------------
    glfwSwapBuffers(window);  // 双缓冲，后缓冲准备好与前缓冲交换，呈现在用户面前
    glfwPollEvents();
  }
  shader->DeleteProgram();
  // glfw: terminate, clearing all previously allocated GLFW resources.
  // ------------------------------------------------------------------
  glfwTerminate();  // 清楚资源
  return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and
// react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow* window) {
  if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
    glfwSetWindowShouldClose(window, true);
  }
}

// glfw: whenever the window size changed (by OS or user resize) this callback function
// executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
  // make sure the viewport matches the new window dimensions; note that width and
  // height will be significantly larger than specified on retina displays.
  glViewport(0, 0, width, height);
}